package cn.iocoder.yudao.framework.mybatis.core.util;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.SortingField;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import com.baomidou.mybatisplus.core.conditions.ISqlSegment;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.support.ColumnCache;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.baomidou.mybatisplus.core.enums.SqlKeyword.*;

/**
 * MyBatis 工具类
 */
public class MyBatisUtils {

    private static final String MYSQL_ESCAPE_CHARACTER = "`";
    private static Method tryInitCache;
    private static Method getColumnCache;
    private static Method appendSqlSegments;

    static {
        LambdaQueryWrapper<Object> queryWrapper = new LambdaQueryWrapper<>();
        try {
            Class<?> superclass = queryWrapper.getClass().getSuperclass();
            tryInitCache = superclass.getDeclaredMethod("tryInitCache", Class.class);
            getColumnCache = superclass.getDeclaredMethod("getColumnCache", String.class, Class.class);
            appendSqlSegments = superclass.getSuperclass().getDeclaredMethod("appendSqlSegments", ISqlSegment[].class);
            tryInitCache.setAccessible(true);
            getColumnCache.setAccessible(true);
            appendSqlSegments.setAccessible(true);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    public static <T> Page<T> buildPage(PageParam pageParam) {
        if (Objects.isNull(pageParam)) {
            pageParam = new PageParam();
        }
        // 页码 + 数量
        Page<T> page = new Page<>(pageParam.getPageNo(), pageParam.getPageSize());
        // 排序字段
        List<SortingField> sortingFields = pageParam.getSortingFields();
        if (CollectionUtil.isEmpty(sortingFields)) {
            sortingFields = PageParam.DEFAULT_SORTING_FIELDS;
        }
        if (null != pageParam.getSortField()) {
            sortingFields=new ArrayList<>();
            sortingFields.add(pageParam.getSortField());
        }
        page.addOrder(sortingFields.stream().map(sortingField ->
                SortingField.ORDER_ASC.equals(sortingField.getOrder()) ?
                        OrderItem.asc(StrUtil.toUnderlineCase(sortingField.getField())) :
                        OrderItem.desc(StrUtil.toUnderlineCase(sortingField.getField()))).collect(Collectors.toList()));
        return page;
    }

    public static <T> Page<T> buildPage(PageParam pageParam, List<SortingField> sortingFields) {
        if (Objects.isNull(pageParam)) {
            pageParam = new PageParam();
            sortingFields = pageParam.getSortingFields();
        }
        if (CollectionUtil.isEmpty(sortingFields)) {
            sortingFields = PageParam.DEFAULT_SORTING_FIELDS;
        }
        // 页码 + 数量
        Page<T> page = new Page<>(pageParam.getPageNo(), pageParam.getPageSize());
        // 排序字段
        page.addOrder(sortingFields.stream().map(sortingField ->
                SortingField.ORDER_ASC.equals(sortingField.getOrder()) ?
                        OrderItem.asc(StrUtil.toUnderlineCase(sortingField.getField())) :
                        OrderItem.desc(StrUtil.toUnderlineCase(sortingField.getField()))).collect(Collectors.toList()));
        return page;
    }

    public static <T> Page<T> buildPage(PageParam pageParam, SortingField sortField) {
        if (Objects.isNull(pageParam)) {
            pageParam = new PageParam();
            sortField = pageParam.getSortField();
        }
        if (Objects.isNull(sortField)) {
            sortField = PageParam.DEFAULT_SORT_FIELD;
        }
        // 页码 + 数量
        Page<T> page = new Page<>(pageParam.getPageNo(), pageParam.getPageSize());
        // 排序字段
        page.addOrder(SortingField.ORDER_ASC.equals(sortField.getOrder()) ?
                OrderItem.asc(StrUtil.toUnderlineCase(sortField.getField())) :
                OrderItem.desc(StrUtil.toUnderlineCase(sortField.getField())));
        return page;
    }


    /**
     * 设置排序字段,如果设置了排序字段,实体类中的orderBy注解将失效
     *
     * @param queryWrapper  {@link LambdaQueryWrapper}
     * @param sortingFields 排序规则
     * @param clazz         排序的对象
     * @param <T>           泛型
     */
    public static <T> void setOrderBy(LambdaQueryWrapper<T> queryWrapper, List<SortingField> sortingFields, Class<?> clazz) {
        if (CollectionUtil.isEmpty(sortingFields)) {
            sortingFields = PageParam.DEFAULT_SORTING_FIELDS;
        }
        sortingFields.forEach(sortingField -> {
                    setOrderOne(queryWrapper, sortingField.getField(), SortingField.ORDER_ASC.equals(sortingField.getOrder()), clazz);
                }
        );
    }

    /**
     * @param queryWrapper {@link LambdaQueryWrapper}
     * @param column       排序字段
     * @param isAsc        是否升序
     * @param clazz        排序的对象
     * @param <T>          泛型
     */
    public static <T> void setOrderOne(LambdaQueryWrapper<T> queryWrapper, String column, boolean isAsc, Class<?> clazz) {
        try {
            tryInitCache.invoke(queryWrapper, clazz);
            ColumnCache columnCache = (ColumnCache) getColumnCache.invoke(queryWrapper, column, clazz);
            if (columnCache == null) {
                throw new RuntimeException("数据库中找不到 " + column + " 映射字段");
            }
            String sqlColumn = columnCache.getColumn();
            ISqlSegment[] sqlSegments = {ORDER_BY, () -> sqlColumn, isAsc ? ASC : DESC};
            appendSqlSegments.invoke(queryWrapper, (Object) sqlSegments);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }


    /**
     * 将拦截器添加到链中
     * 由于 MybatisPlusInterceptor 不支持添加拦截器，所以只能全量设置
     *
     * @param interceptor 链
     * @param inner       拦截器
     * @param index       位置
     */
    public static void addInterceptor(MybatisPlusInterceptor interceptor, InnerInterceptor inner, int index) {
        List<InnerInterceptor> inners = new ArrayList<>(interceptor.getInterceptors());
        inners.add(index, inner);
        interceptor.setInterceptors(inners);
    }

    /**
     * 获得 Table 对应的表名
     * <p>
     * 兼容 MySQL 转义表名 `t_xxx`
     *
     * @param table 表
     * @return 去除转移字符后的表名
     */
    public static String getTableName(Table table) {
        String tableName = table.getName();
        if (tableName.startsWith(MYSQL_ESCAPE_CHARACTER) && tableName.endsWith(MYSQL_ESCAPE_CHARACTER)) {
            tableName = tableName.substring(1, tableName.length() - 1);
        }
        return tableName;
    }

    /**
     * 构建 Column 对象
     *
     * @param tableName  表名
     * @param tableAlias 别名
     * @param column     字段名
     * @return Column 对象
     */
    public static Column buildColumn(String tableName, Alias tableAlias, String column) {
        return new Column(tableAlias != null ? tableAlias.getName() + "." + column : column);
    }

}
